/**
 * create by zhangpu
 * date:2015年3月20日
 */
package com.acooly.module.openapi.client.provider.bosc;

import com.acooly.core.utils.Strings;
import com.acooly.module.openapi.client.api.anotation.ApiMsg;
import com.acooly.module.openapi.client.api.enums.ApiMessageType;
import com.acooly.module.openapi.client.api.message.ApiMessage;
import com.acooly.module.openapi.client.api.message.MessageFactory;
import com.acooly.module.openapi.client.api.message.MessageMeta;
import com.acooly.module.openapi.client.api.notify.NotifyHandler;
import com.acooly.module.openapi.client.provider.bosc.domain.ApiMsgInfo;
import com.acooly.module.openapi.client.provider.bosc.domain.BoscMessage;
import com.acooly.module.openapi.client.provider.bosc.domain.BoscResponse;
import com.acooly.module.openapi.client.provider.bosc.enums.BoscServiceNameEnum;
import com.acooly.module.openapi.client.provider.bosc.message.member.EnterpriseRegisterNotify;
import com.acooly.module.openapi.client.provider.bosc.message.member.PersonalRegisterNotify;
import com.acooly.module.openapi.client.provider.bosc.message.member.QueryUserInfomationResponse;
import com.acooly.module.openapi.client.provider.bosc.message.trade.UserAutoPreTransactionRequest;
import com.acooly.module.openapi.client.provider.bosc.message.trade.UserAutoPreTransactionResponse;
import com.google.common.collect.Maps;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.stereotype.Service;

import java.lang.reflect.Modifier;
import java.net.URL;
import java.util.Map;

/**
 * @author zhangpu
 */
@Service
public class BoscMessageFactory implements MessageFactory,InitializingBean {
    
    private Logger logger = LoggerFactory.getLogger (BoscMessageFactory.class);
    
    public Map<String, MessageMeta> metas = Maps.newHashMap();
    
    private String messagePackage = "classpath*:com.acooly.module.openapi.client.provider.bosc.message";
    
    @Override
    public ApiMessage getRequest(String serviceName) {
        return newInstance(metas.get(serviceName).getRequest());
    }
    
    @Override
    public ApiMessage getResponse(String serviceName) {
        return newInstance(metas.get(serviceName).getResponse());
    }
    
    @Override
    public ApiMessage getNotify(String serviceName) {
        return newInstance(metas.get(serviceName).getAsyncNotify());
    }
    
    @Override
    public ApiMessage getReturn(String serviceName) {
        return newInstance(metas.get(serviceName).getSyncNotify());
    }
    
    
    
    @Override
    public void afterPropertiesSet() throws Exception {
        init();
    }
    
    
    @SuppressWarnings("unchecked")
    public void init() {
        ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver ();
        try {
            Resource[] resources = resourcePatternResolver.getResources(getResourcePattern());
            ClassLoader loader = this.getClass().getClassLoader();
            String binaryName = null;
            for (Resource resource : resources) {
                binaryName = getCanonicalClassPath(resource);
                try {
                    Class<?> clazz = loader.loadClass(binaryName);
                    if (Modifier.isAbstract(clazz.getModifiers())) {
                        continue;
                    }
                    ApiMsgInfo apiMsg = getAnotation(clazz);
                    if (apiMsg != null) {
                        register(apiMsg,(Class<? extends ApiMessage>)clazz);
                    }
                } catch (Exception e) {
                    continue;
                }
            }
        } catch (Exception e) {
            throw new RuntimeException("扫描Message失败:" + e.getMessage());
        }
    }
    
    
    protected void register(ApiMsgInfo apiMsg, Class<? extends ApiMessage> clazz){
        logger.info ("注册Message:{}",apiMsg);
        String service = apiMsg.service().getCode ();
        if(metas.get(service) == null){
            metas.put(service, new MessageMeta());
        }
        if(apiMsg.type() == ApiMessageType.Request){
            metas.get(service).setRequest(clazz);
        }else if(apiMsg.type() == ApiMessageType.Response){
            metas.get(service).setResponse(clazz);
        }else if(apiMsg.type() == ApiMessageType.Return){
            metas.get(service).setSyncNotify(clazz);
        }else if(apiMsg.type() == ApiMessageType.Notify){
            metas.get(service).setAsyncNotify(clazz);
        }
    }
    
    
    protected ApiMsgInfo getAnotation(Class<?> clazz) {
        return clazz.getAnnotation(ApiMsgInfo.class);
    }
    
    protected String getCanonicalClassPath(Resource resource) {
        try {
            URL classFileUrl = resource.getURL();
            String path = classFileUrl.getPath();
            if (Strings.contains(path, "classes/")) {
                path = StringUtils.substringAfter(path, "classes/");
            }
            if (Strings.contains(path, "jar!/")) {
                path = StringUtils.substringAfterLast (path, "jar!/");
            }
            path = path.replaceAll("/", ".").replace(".class", "");
            return path;
        } catch (Exception e) {
            throw new RuntimeException(e.getMessage());
        }
    }
    
    private String getResourcePattern() {
        return Strings.replace(messagePackage, ".", "/") + "/**/*.class";
    }
    
    private <T> T newInstance(Class<T> clazz) {
        try {
            return (T) clazz.newInstance();
        } catch (InstantiationException e) {
            throw new RuntimeException("InstantiationException:" + clazz);
        } catch (IllegalAccessException e) {
            throw new RuntimeException("IllegalAccessException:" + clazz);
        }
    }
    
    public void setMessagePackage(String messagePackage) {
        this.messagePackage = messagePackage;
    }
}
